/*! \file
 * \brief Enter a brief description here.
 *
 * Enter a detailed description here. (optional)
 * \author Author (Ivo Wingelaar)
 * \date Date (2011)
 * \version Version (0.0.1)
 * \copyright Copyright (GNU LGPL v3)
 */

#include "LynxCursorManagerLinux_C.hpp"

#ifdef _LYNX_LINUX_

namespace Lynx
{

/** constructor **/
/// \param    WindowManagerLinux_C* window
///         A pointer to a linux windowmanager.
CursorManagerLinux_C::CursorManagerLinux_C(Display** display_ptr, Window* window_ptr, uint32_t* width_ptr, uint32_t* height_ptr) :
    mDisplay(display_ptr),
    mWindow(window_ptr),
    mWidth(width_ptr),
    mHeight(height_ptr),
    mIsVisible(true),
    mUseReferenceRect(false)
{
    XGCValues values;
    unsigned long valuemask = 0;

    XColor fg, bg;

    // this code, for making the cursor invisible was sent in by
    // Sirshane, thank your very much!

    Pixmap invisBitmap = XCreatePixmap(*mDisplay, *mWindow, 32, 32, 1);
    Pixmap maskBitmap = XCreatePixmap(*mDisplay, *mWindow, 32, 32, 1);
    Colormap screen_colormap = DefaultColormap(*mDisplay, DefaultScreen(*mDisplay));
    XAllocNamedColor(*mDisplay, screen_colormap, "black", &fg, &fg);
    XAllocNamedColor(*mDisplay, screen_colormap, "white", &bg, &bg);

    GC gc = XCreateGC(*mDisplay, invisBitmap, valuemask, &values);

    XSetForeground(*mDisplay, gc, BlackPixel(*mDisplay, DefaultScreen(*mDisplay)));
    XFillRectangle(*mDisplay, invisBitmap, gc, 0, 0, 32, 32);
    XFillRectangle(*mDisplay, maskBitmap, gc, 0, 0, 32, 32);

    mInvisCursor = XCreatePixmapCursor(*mDisplay, invisBitmap, maskBitmap, &fg, &bg, 1, 1);
    XFreeGC(*mDisplay, gc);
    XFreePixmap(*mDisplay, invisBitmap);
    XFreePixmap(*mDisplay, maskBitmap);
}

/** default destructor **/
CursorManagerLinux_C::~CursorManagerLinux_C()
{
    //
}

/** virtual function **/
/** set_visible      **/
//! Sets if the cursor should be visible.
/// \return void
/// \param    bool visible
///         True if you want the cursor to be visible, false otherwise.
void CursorManagerLinux_C::set_visible(bool visible)
{
    if(visible==mIsVisible)
        return;
    mIsVisible = visible;
    if(!mIsVisible)
        XDefineCursor(*mDisplay, *mWindow, mInvisCursor);
    else
        XUndefineCursor(*mDisplay, *mWindow);
}

/** const virtual function **/
/** is_visible             **/
//! Returns if the cursor is visible.
/// \return bool
///         True if the cursor is visible, false otherwise.
/// \param  void
bool CursorManagerLinux_C::is_visible(void) const
{
    return mIsVisible;
}

/** virtual function **/
/** set_position     **/
//! Sets the position of the cursor.
/// \return void
/// \param    const Core::Vector2f& pos
///         The relative position of the cursor on the screen.
void CursorManagerLinux_C::set_position(const Myth::Math::Vector2f& pos)
{
    set_position(pos.X, pos.Y);
}

/** virtual function **/
/** set_position     **/
//! Sets the position of the cursor.
/// \return void
/// \param    float x
///         The relative x-coord of the cursor.
/// \param    float y
///         The relative y-coord of the cursor.
void CursorManagerLinux_C::set_position(float x,
                                        float y)
{
    set_position((int32_t)(x** mWidth), (int32_t)(y** mHeight));
}

/** virtual function **/
/** set_position     **/
//! Sets the position of the cursor.
/// \return void
/// \param    const Core::Vector2i& pos
///         The absolute position of the cursor on the screen.
void CursorManagerLinux_C::set_position(const Myth::Math::Vector2i& pos)
{
    set_position(pos.X, pos.Y);
}

/** virtual function **/
/** set_position     **/
//! Sets the position of the cursor.
/// \return void
/// \param    int32_t x
///         The absolute x-coord of the cursor.
/// \param    int32_t y
///         The absolute y-coord of the cursor.
void CursorManagerLinux_C::set_position(int32_t x,
                                        int32_t y)
{
    if(mUseReferenceRect)
    {
        XWarpPointer(*mDisplay,
                     None,
                     *mWindow, 0, 0,
                     *mWidth,
                     *mHeight,
                     mReferenceRect.UpperLeftCorner.X + x,
                     mReferenceRect.UpperLeftCorner.Y + y);
    }
    else
    {
        XWarpPointer(*mDisplay,
                     None,
                     *mWindow, 0, 0,
                     *mWidth,
                     *mHeight, x, y);
    }
    XFlush(*mDisplay);
    mCursorPos.X = x;
    mCursorPos.Y = y;
}

/** const virtual function **/
/** get_position           **/
//! Returns the absolute position of the cursor.
/// \return Core::Vector2i
///         The absolute position of the cursor.
/// \param  void
Myth::Math::Vector2i CursorManagerLinux_C::get_position(void)
{
    update_cursor_pos();
    return mCursorPos;
}

/** const virtual function **/
/** get_relative_position  **/
//! Returns the relative position of the cursor.
/// \return Core::Vector2f
///         The relative position of the cursor.
/// \param  void
Myth::Math::Vector2f CursorManagerLinux_C::get_relative_position(void)
{
    update_cursor_pos();

    if(!mUseReferenceRect)
        return Myth::Math::Vector2f(mCursorPos.X / (float)*mWidth, mCursorPos.Y / (float)*mHeight);

    return Myth::Math::Vector2f(mCursorPos.X / (float)mReferenceRect.get_width(), mCursorPos.Y / (float)mReferenceRect.get_height());
}

/** virtual function   **/
/** set_reference_rect **/
//! Sets a new reference rectangle for the cursor positions.
/// \return void
/// \param    Core::Rectanglei* rect
///         A pointer to the new reference rectangle for the relative and absolute positions.
void CursorManagerLinux_C::set_reference_rect(Myth::Math::Rectanglei* rect)
{
    if(rect)
    {
        mReferenceRect = *rect;
        mUseReferenceRect = true;

        // prevent division through zero and uneven sizes

        if(!mReferenceRect.get_height() || mReferenceRect.get_height()%2)
            mReferenceRect.LowerRightCorner.Y += 1;

        if(!mReferenceRect.get_width() || mReferenceRect.get_width()%2)
            mReferenceRect.LowerRightCorner.X += 1;
    }
    else
        mUseReferenceRect = false;
}

/** function          **/
/** update_cursor_pos **/
//! Updates the position of the cursor.
/// \return void
/// \param  void
void CursorManagerLinux_C::update_cursor_pos(void)
{
    Window tmp;
    int itmp1, itmp2;
    unsigned  int maskreturn;
    XQueryPointer(*mDisplay, *mWindow,
                  &tmp, &tmp,
                  &itmp1, &itmp2,
                  &mCursorPos.X, &mCursorPos.Y, &maskreturn);

    if(mCursorPos.X < 0)
        mCursorPos.X = 0;
    if(mCursorPos.X > (int32_t) *mWidth)
        mCursorPos.X = *mWidth;
    if(mCursorPos.Y < 0)
        mCursorPos.Y = 0;
    if(mCursorPos.Y > (int32_t)*mHeight)
        mCursorPos.Y = *mHeight;
}

} // namespace Lynx

#endif // _LYNX_LINUX_
